package com.dingxin.ship_together_api.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.alibaba.fastjson.JSON;
import com.dingxin.ship_together_api.entity.OrderInfo;
import com.dingxin.ship_together_api.entity.enums.OrderStatusEnum;
import com.dingxin.ship_together_api.service.OrderInfoService;
import com.dingxin.ship_together_api.service.WxPayService;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.util.Map;

/**
 * Author: ctj
 * Date: 2023-01-09
 * Time: 10:43
 */
@Service
public class WxPayServiceImpl implements WxPayService {
    /** 商户API私钥路径 */
    @Value("${wxpay.privateKeyPath}")
    private String privateKeyPath;

    /** 商户证书序列号 */
    @Value("${wxpay.merchantSerialNumber}")
    private String merchantSerialNumber;

    /** 商户APIV3密钥 */
    @Value("${wxpay.apiV3Key}")
    private String apiV3Key;

    /** appId */
    @Value("${wxpay.appId}")
    private String appId;

    /** 商户号 */
    @Value("${wxpay.mchId}")
    private String mchId;

    /** notifyUrl */
    @Value("${wxpay.notifyUrl}")
    private String notifyUrl;

    private Verifier verifier; // 验签器
    private static CertificatesManager certificatesManager; // 平台证书管理器

    private static Config config;
    private static JsapiServiceExtension jsapiServiceExtension;

    @Lazy
    @Autowired
    private OrderInfoService orderInfoService;

    @Override public PrepayWithRequestPaymentResponse  genPrePayInfo(OrderInfo orderInfo) {
        initConfig();
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(1);
        request.setAmount(amount);
        // 暂时写死为1分
        request.setAppid(appId);
        request.setMchid(mchId);
        request.setDescription("商品标题");
        request.setNotifyUrl(notifyUrl);
        request.setOutTradeNo(orderInfo.getId().toString());
        Payer payer = new Payer();
        payer.setOpenid(StpUtil.getSession().get("openId").toString());
        request.setPayer(payer);
        PrepayWithRequestPaymentResponse paymentResponse = jsapiServiceExtension.prepayWithRequestPayment(request);
        return paymentResponse;
    }

    @Override public SaResult callback(HttpServletRequest request) throws Exception {
        //从请求头获取验签字段
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String signature = request.getHeader("Wechatpay-Signature");
        String serial = request.getHeader("Wechatpay-Serial");
        ServletInputStream inputStream = request.getInputStream();
        StringBuffer stringBuffer = new StringBuffer();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s;
        //读取回调请求体
        while ((s = bufferedReader.readLine()) != null) {
            stringBuffer.append(s);
        }
        String s1 = stringBuffer.toString();
        if (verifier == null) {
            setup();
        }
        // 构建request，传入必要参数
        NotificationRequest parseRequest = new NotificationRequest.Builder().withSerialNumber(serial)
            .withNonce(nonce)
            .withTimestamp(timestamp)
            .withSignature(signature)
            .withBody(s1)
            .build();
        NotificationHandler handler = new NotificationHandler(verifier, apiV3Key.getBytes(StandardCharsets.UTF_8));
        // 验签和解析请求体
        Notification notification = handler.parse(parseRequest);
        Map<String,Object> map = JSON.parseObject(notification.getDecryptData(), Map.class);
        String outTradeNo = (String)map.get("out_trade_no");
        String transactionId = (String)map.get("transaction_id");
        String tradeState = (String)map.get("trade_state");
        String openid = (String)(JSON.parseObject(map.get("payer").toString(), Map.class).get("openid"));
        String total = String.valueOf(JSON.parseObject(String.valueOf(map.get("amount")), Map.class).get("total"));
        // 修改订单状态
        OrderInfo order = orderInfoService.getById(outTradeNo);
        if ("SUCCESS".equals(tradeState) &&
            order!=null &&
            // order.getAmount().toString().equals(total)
            // todo:正式上线时删除下面两行校验，使用上面一行
            order.getAmount().toString().equals("10000") &&
            total.equals("1")
        ) {
            order.setStatus(OrderStatusEnum.PAYED.getCode());
            orderInfoService.updateById(order);
        }
        return SaResult.ok();
    }

    /**
     * 初始化配置
     */
    private void initConfig() {
        if (config == null) {
            config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(mchId)
                    .privateKeyFromPath(privateKeyPath)
                    .merchantSerialNumber(merchantSerialNumber)
                    .apiV3Key(apiV3Key)
                    .build();
        }
        if (jsapiServiceExtension == null) {
            jsapiServiceExtension = new JsapiServiceExtension.Builder().config(config).build();
        }
    }

    /**
     * 初始化平台证书管理器
     * @throws Exception
     */
    private void setup() throws Exception {
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(privateKeyPath));
        // 获取证书管理器实例
        certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
                new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
            apiV3Key.getBytes(StandardCharsets.UTF_8));
        verifier = certificatesManager.getVerifier(mchId);
    }
}
